#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

const int basesize = 1024;
const int envnum = 64;
const int argvnum = 64;
//定义一个全局的命令行参数表
char *gargv[argvnum];
char gargc = 0;

int lastcode = 0;//最后一次执行程序的退出码

//我系统自己的环境变量
char* genv[envnum];

//定义一个我自己的全局的shell工作路径
char pwd[basesize];
char pwdenv[basesize*basesize];

string GetUserName()
{
    const char* name = getenv("USER");
    //return name.empty() ? "None" : name;
    return name ? string(name) : "None";
}

string GetHostName()
{
    const char* hostname = getenv("HOSTNAME");
    //return hostname.empty() ? "None" : hostname;
    return hostname ? string(hostname) : "None";
}


//从环境变量中获取
string GetPwd()
{
    if(nullptr == getcwd(pwd, sizeof(pwd))) return "None";//getcwd获取当前工作目录的路径，并复制到pwd中
    snprintf(pwdenv, sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);//putcwd用于创建或修改环境变量
    return pwd;


    //const char* pwd = getenv("PWD");
    //return pwd ? string(pwd) : "None";
}

string LastDir()
{
    string curr = GetPwd();
    if(curr == "/" || curr == "None") return curr;
    size_t pos = curr.rfind("/");
    return curr.substr(pos+1);
}


string MakeCommandLine()
{
    //[jhual@huaweicloud]:$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@jhualcloud %s]", GetUserName().c_str(), LastDir().c_str());
    return command_line;
}

void PrintCommandLine()//1.打印命令行提示符
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}

bool GetCommandLine(char command_buffer[], int size)//获取用户命令
{
    //输入的命令行，当做一个完整的字符串
    //如"ls -a -l"
    char *result = fgets(command_buffer, size, stdin);//输入命令
    if(!result)
	    return false;
    command_buffer[strlen(command_buffer)-1] = 0;//最后一个是'\n',把它置为0，使字符串只是包括命令
    if(strlen(command_buffer)==0) return false;
    return true;
}

void ParseCommandLine(char command_buffer[], int len)//3.分析命令
{
    memset(gargv, 0, sizeof(gargv));//先将gargv进行初始化
    gargc = 0;
    //"ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);//strtok是分割字符串的函数，成功时返回指向下一个标记的指针
    while((bool)(gargv[gargc++] = strtok(nullptr, sep)));
    gargc--;//把最后的nullptr也加上去了，所以要减掉

}


//在shell中
//有些命令，必须由子进程来执行
//有些命令，不能由子进程执行，要由shell自己执行————内建命令
bool ExecuteCommand()
{
    //让子进程执行命令
    pid_t id = fork();
    if(id == 0)
    {
	//子进程执行命令
	    execvpe(gargv[0], gargv, genv);
	    exit(1);//退出
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);//等待子进程退出
    if(rid > 0)//等待成功
    {
	    if(WIFEXITED(status))
	        lastcode = WEXITSTATUS(status);
	    else
	        lastcode = 100;
	    return true;
    }
    return false;
}

void AddEnv(const char* item)
{
    int index = 0;
    while(genv[index])
        index++;
    genv[index] = (char*)malloc(strlen(item)+1);
    strncpy(genv[index], item, strlen(item)+1);
    genv[++index] = nullptr;
}

bool CheakAndExecBuiltCommand()
{
    if(strcmp(gargv[0], "cd") == 0)
    {
	    if(gargc == 2)
	    {
	        //内建命令，直接让shell调用自己的函数
	        chdir(gargv[1]);
	        lastcode = 0;
	    }
	    else
	        lastcode = 1;
        return true;
    }
    else if(strcmp(gargv[0], "export") == 0)
    {
        if(gargc == 2)
        {
            AddEnv(gargv[1]);
            lastcode = 0;
        }
        else
            lastcode = 2;
        return true;
    }
    else if(strcmp(gargv[0], "env") == 0)
    {
        for(int i = 0; genv[i]; i++)
        {
            printf("env[%d]:%s\n", i, genv[i]);
        }
        lastcode = 0;
        return true;
    }
    else if(strcmp(gargv[0], "echo") == 0)
    {
        if(gargc == 2)
        {
            if(gargv[1][0] == '$')
            {
                if(gargv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
            }
            else
            {
                printf("%s", gargv[1]);
                lastcode = 0;
            }
        }
        else
            lastcode = 3;
        return true;
    }
    
    return false;
}

//作为一个shell，获取环境变量要从系统的配置表中获取
//但我们今天直接从父shell中提取环境变量
//所以我们要把系统的环境变量拷贝到自己父shell的环境变量中，使得可以改变
void Initenv()
{
    extern char** environ;//首先声明全局变量，它指向一张环境变量表
    //环境表是一个字符指针数组，每个指针指向一个以'\0'结尾的字符串，表结尾还有nullptr
    int index = 0;
    while(environ[index])
    {
	    genv[index] = (char*)malloc(strlen(environ[index]+1));//加1是加上\0的长度
	    strncpy(genv[index], environ[index],strlen(environ[index])+1);
	    index++;
    }
    genv[index] = nullptr;

}

int main()
{
    Initenv();//获取环境变量
    char command_buffer[basesize];//存放命令的数组
    while(true)
    {
	    PrintCommandLine();//1.命令行提示符
	    if(!GetCommandLine(command_buffer, basesize))
	    {
	        continue;

	    }//2.获取用户命令
	    //printf("%s\n",command_buffer);				     
						     
	    ParseCommandLine(command_buffer, strlen(command_buffer));//3.分析命令
	    if(CheakAndExecBuiltCommand())//是否是内建命令
	    {
	        continue;
	    }
	    ExecuteCommand();//4.执行命令
	
    }
    return 0;
}
